package edu.cmu.cs211.exprtree.ast.util;

import java.awt.GraphicsEnvironment;
import java.io.File;
import java.io.IOException;
import java.util.Formatter;

import edu.cmu.cs211.exprtree.ast.BinaryExpression;
import edu.cmu.cs211.exprtree.ast.ConstantExpression;
import edu.cmu.cs211.exprtree.ast.Expression;
import edu.cmu.cs211.exprtree.ast.UnaryExpression;

/** A utility class to build a `dot' style graph from an Expression */
public final class ExpressionVisualizer {
	private Formatter out;
	private int id = 0;

	/**
	 * Builds a string that contains a `dot' style graph readable by <a
	 * href="http://www.graphviz.org/">GraphViz</a>.
	 */
	public static String toGraph (Expression e)
	{
		Formatter f = new Formatter ();
		new ExpressionVisualizer (e, f);
		return f.out ().toString ();
	}

	/**
	 * Generates an image of a given expression and generates
	 * an image. it then displays it.
	 */
	public static void display (Expression e)
	{
		// make sure this isn't used on FrontDesk
		if (GraphicsEnvironment.isHeadless ())
			return;
		try {
			File dot = File.createTempFile ("dotout", ".dot");
			new ExpressionVisualizer (e, new Formatter (dot));
			File png = File.createTempFile ("dotout", ".png");
			run ("dot", "-Tpng", "-o", png.getAbsolutePath (), dot
			        .getAbsolutePath ());
			
			// eog is a Gnome image viewer. If you don't have this,
			// enter the path to a program that displays images.
			run ("eog", png.getAbsolutePath ());
			dot.delete ();
			png.delete ();
		} catch (Exception ex) {
			throw new RuntimeException (ex);
		}
	}

	private static void run (String... args) throws InterruptedException,
	        IOException
	{
		new ProcessBuilder (args).start ().waitFor ();
	}

	private ExpressionVisualizer (Expression e, Formatter f) {
		this.out = f;

		out.format ("digraph {\n");
		dobuild (e);
		out.format ("}");
		f.flush ();
	}

	private void printNode (String name, String lbl)
	{
		out.format ("\t%s[label = \"%s\"]\n", name, lbl);
	}

	private void printEdge (String t, String h)
	{
		out.format ("\t%s -> %s\n", t, h);
	}

	private String dobuild (Expression e)
	{
		String nm = "node_" + (++id);

		if (e instanceof BinaryExpression) {
			BinaryExpression be = (BinaryExpression) e;
			printNode (nm, "" + be.getOperatorSymbol ());
			printEdge (nm, dobuild (be.getLeft ()));
			printEdge (nm, dobuild (be.getRight ()));
		} else if (e instanceof UnaryExpression) {
			UnaryExpression ue = (UnaryExpression) e;
			printNode (nm, "" + ue.getOperatorSymbol ());
			printEdge (nm, dobuild (ue.getOperand ()));
		} else if (e instanceof ConstantExpression) {
			ConstantExpression ce = (ConstantExpression) e;
			printNode (nm, "" + ce.getValue ());
		} else {
			printNode (nm, "" + e);
		}

		return nm;
	}
}
